
package com.scs.application.modules.base.service.impl;

import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.model.TreeModel;
import com.scs.application.core.model.TreeNode;
import com.scs.application.core.service.TreeServiceImpl;
import com.scs.application.core.utils.*;
import com.scs.application.modules.base.dto.ExcelMaterialCatalogDTO;
import com.scs.application.modules.base.entity.Material;
import com.scs.application.modules.base.entity.MaterialCatalog;
import com.scs.application.modules.base.excel.handler.ExcelImportUtilCustom;
import com.scs.application.modules.base.mapper.MaterialCatalogMapper;
import com.scs.application.modules.base.request.MaterialCatalogQueryRequest;
import com.scs.application.modules.base.service.MaterialCatalogService;
import com.scs.application.modules.base.service.MaterialService;
import com.scs.application.modules.base.vo.MaterialCatalogVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.ConstraintViolationException;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import java.util.Map;


/**
 * 商品品类 service 实现
 */
@Slf4j
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class MaterialCatalogServiceImpl extends TreeServiceImpl<MaterialCatalogMapper, MaterialCatalog> implements MaterialCatalogService {
    private final String materialCatalogChildSort = "materialCatalogChildSort";

    @Override
    public boolean saveCascadeById(MaterialCatalog entity) {
        List<MaterialCatalog> codeList = this.list(Wrappers.<MaterialCatalog>query().eq("code", entity.getCode()));
        if (codeList.size() > 0) {
            throw new BusinessException("品类代码不能重复");
        }
        return super.saveCascadeById(entity);
    }

    @Override
    public boolean deleteById(String id) {
        //是否有下属商品
        int count = this.count(Wrappers.<MaterialCatalog>query().eq("parent_id", id));
        if (count > 0) {
            throw new BusinessException("已经有下属商品的品类不能删除");
        }
        return this.removeCascadeById(id);
    }

    @Override
    public IPage page(MaterialCatalogQueryRequest request) {
        return this.baseMapper.page(request.getPage(), request);
    }

    @Override
    public IPage pageForSupplier(MaterialCatalogQueryRequest request) {
        request.setSupplierId(UserUtils.getSupplierId());
        return this.baseMapper.pageForSupplier(request.getPage(), request);
    }

    @Override
    public List<MaterialCatalog> list(MaterialCatalogQueryRequest request) {
        List<MaterialCatalog> list = this.list(Wrappers.<MaterialCatalog>query()
                .orderByDesc("sort")
        );
        return list;
    }

    @Override
    public List<MaterialCatalog> listForSupplier(MaterialCatalogQueryRequest request) {
        request.setSupplierId(UserUtils.getSupplierId());
        return this.baseMapper.listForSupplier(request);
    }

    @Override
    public List<MaterialCatalogVO> exportList(MaterialCatalogQueryRequest request) {
        List<MaterialCatalogVO> list = baseMapper.exportList(request);
        return list;
    }


    @Override
    protected ExcelImportResult doExcelImport(MultipartFile multipartFile, Map<String, Object> params) throws Exception {
        /*ImportParams importParams = PoiUtils.getDefaultImportParams();
        ExcelImportResult importResult = ExcelImportUtilCustom.importExcelMore(multipartFile.getInputStream(), ExcelMaterialCatalogDTO.class, importParams);
        List<ExcelMaterialCatalogDTO> dtoList = Lists.newArrayList(importResult.getList());
        List<MaterialCatalog> result = Lists.newArrayListWithCapacity(dtoList.size());
        DefaultSaveHandler<ExcelMaterialCatalogDTO> saveHandler = new DefaultSaveHandler<ExcelMaterialCatalogDTO>() {
            @Override
            public void doSave(int currentIndex, ExcelMaterialCatalogDTO entity) throws BusinessException {
                MaterialCatalog mc = new MaterialCatalog();
                BeanUtils.copyProperties(entity, mc);

                if(mc.getParentCode()!=null && !mc.getParentCode().isEmpty()){
                    //查询数据库是否有该品类，如果有就更新，没有就保存
                    MaterialCatalog materialCatalog = getOne(Wrappers.<MaterialCatalog>query()
                            .eq("code", mc.getParentCode()).last("limit 1")
                    );
                    if(materialCatalog == null){
                        importResult.getList().remove(entity);
                        entity.setErrorMsg(String.format("父级品类代码不存在"));
                        importResult.getFailList().add(entity);
                        log.warn("第{}行父级品类代码不存在，请先确认数据是否正确；父级品类代码：{}", currentIndex, mc.getParentCode());
                        return;
                    }else{
                        mc.setParentId(materialCatalog.getId()).setParentIds(mc.getParentIds()+materialCatalog.getParentId()+",");
                    }

                }

                //查询数据库是否有该品类，如果有就更新，没有就保存
                MaterialCatalog materialCatalog = getOne(Wrappers.<MaterialCatalog>query()
                        .eq("code", mc.getCode())
                        .eq("name", mc.getName()).last("limit 1")
                );
                if (materialCatalog != null) {
                    mc.setId(materialCatalog.getId());
                }
                result.add(mc);

            }
        };
        BatchSaveTask<ExcelManufacturerDTO> task = new BatchSaveTask(saveHandler, dtoList);

        this.forkJoinPool.invoke(task);

        if(!result.isEmpty()){
            List<MaterialCatalog> updateMaterialCatalogs = result.stream().filter(o -> StringUtils.isNotBlank(o.getId())).collect(Collectors.toList());
            List<MaterialCatalog> insertMaterialCatalogs = result.stream().filter(o -> StringUtils.isBlank(o.getId())).collect(Collectors.toList());
            if (!updateMaterialCatalogs.isEmpty()) {
                this.updateBatchById(updateMaterialCatalogs);
            }
            if (!insertMaterialCatalogs.isEmpty()) {
                this.saveBatch(insertMaterialCatalogs);
            }
        }*/

        ImportParams importParams = PoiUtils.getDefaultImportParams();
        ExcelImportResult importResult = ExcelImportUtilCustom.importExcelMore(multipartFile.getInputStream(), ExcelMaterialCatalogDTO.class, importParams);
        List<ExcelMaterialCatalogDTO> dtoList = Lists.newArrayList(importResult.getList());
        //List<MaterialCatalog> result = Lists.newArrayListWithCapacity(dtoList.size());
        ExcelImportResult result = new ExcelImportResult();

        if (CollectionUtils.isEmpty(dtoList)) {
            return importResult;
        }

        List<ExcelMaterialCatalogDTO> filtedList = Lists.newArrayList();

        List<String> messageList = Lists.newArrayList();

        // 初步数据校验
        int currentIndex = 2;
        for (ExcelMaterialCatalogDTO materialCatalogDTO : dtoList) {
            try {
                BeanValidators.validateWithException(validator, materialCatalogDTO);
                filtedList.add(materialCatalogDTO);
            } catch (ConstraintViolationException e) {
                importResult.getList().remove(materialCatalogDTO);
                importResult.getFailList().add(materialCatalogDTO);
                messageList.add(MessageFormat.format("第 [{0}] 行数据校验错误：{1}", currentIndex++, StringUtils.join(BeanValidators.extractMessage(e.getConstraintViolations()))));
            }
        }

       /* List<MaterialCatalog> catalogs = this.list();
        for (MaterialCatalog catalog : catalogs) {
            CacheUtils.hashPut("data:base:materialCatalog:code", catalog.getCode(), catalog);
        }*/

        TreeModel<ExcelMaterialCatalogDTO> treeModel = new TreeModel<ExcelMaterialCatalogDTO>(filtedList, "code", "name", "parentCode", "parentCode", null) {
            @Override
            protected void addExtraProperties(TreeNode node, ExcelMaterialCatalogDTO entity) {
                node.addProperty("dto", entity);
            }
        };
        List<TreeNode> nodeList = treeModel.asList();
        for (TreeNode node : nodeList) {
            try {
                addCatalogByNode(node);
            } catch (ConstraintViolationException e) {
                messageList.add(MessageFormat.format("品类编码[{0}],数据校验错误", node.getId(), StringUtils.join(BeanValidators.extractMessage(e.getConstraintViolations()))));
            }
        }

        return importResult;
    }


    /*@Override
    public ImportResult excelImport(MultipartFile file) {
        ImportResult result = new ImportResult();
        try {

            StopWatch stopWatch = new StopWatch();
            stopWatch.start();

            // 读取Excel数据
            ImportExcel ei = new ImportExcel(file, 0, 0);
            List<MaterialCatalogDTO> list = ei.getDataList(MaterialCatalogDTO.class);
            if (CollectionUtils.isEmpty(list)) {
                return result;
            }

            List<MaterialCatalogDTO> filtedList = Lists.newArrayList();

            List<String> messageList = Lists.newArrayList();

            // 初步数据校验
            int currentIndex = 2;
            for (MaterialCatalogDTO materialCatalogDTO : list) {
                try {
                    BeanValidators.validateWithException(validator, materialCatalogDTO);
                    filtedList.add(materialCatalogDTO);
                } catch (ConstraintViolationException e) {
                    messageList.add(MessageFormat.format("第 [{0}] 行数据校验错误：{1}", currentIndex++, StringUtils.join(BeanValidators.extractMessage(e.getConstraintViolations()))));
                }
            }


            List<MaterialCatalog> catalogs = this.list();
            for (MaterialCatalog catalog : catalogs) {
                CacheUtils.hashPut("data:base:materialCatalog:code", catalog.getCode(), catalog);
            }

            TreeModel<MaterialCatalogDTO> treeModel = new TreeModel<MaterialCatalogDTO>(filtedList, "code", "name", "parentCode", "parentCode", null) {
                @Override
                protected void addExtraProperties(TreeNode node, MaterialCatalogDTO entity) {
                    node.addProperty("dto", entity);
                }
            };
            int successCount = 0;
            List<TreeNode> nodeList = treeModel.asList();
            for (TreeNode node : nodeList) {
                try {
                    addCatalogByNode(node);
                    successCount++;
                } catch (ConstraintViolationException e) {
                    messageList.add(MessageFormat.format("品类编码[{0}],数据校验错误", node.getId(), StringUtils.join(BeanValidators.extractMessage(e.getConstraintViolations()))));
                }
            }

            stopWatch.stop();

            result.setTotalCount(list.size())
                    .setSuccessCount(successCount)
                    .setFailCount(messageList.size())
                    .setElapsedSecond(stopWatch.getTotalTimeSeconds())
                    .setMessages(messageList);

            log.info("本次导入共处理{}条数据，成功：{}条,失败：{},耗时：{}秒",
                    result.getTotalCount(), result.getSuccessCount(), result.getFailCount(), result.getElapsedSecond());
        } catch (Exception e) {
            log.error("品类导入失败", e);
            throw new BusinessException("品类导入失败");
        }

        return result;
    }*/


    private void addCatalogByNode(TreeNode node) {

        ExcelMaterialCatalogDTO dto = (ExcelMaterialCatalogDTO) node.getProperties().get("dto");

        // MaterialCatalog entity = CacheUtils.hashGet("data:base:materialCatalog:code", dto.getCode());
        MaterialCatalog entity = baseMapper.selectOne(Wrappers.<MaterialCatalog>query().eq("code", dto.getCode()));

        if (entity == null) {
            entity = new MaterialCatalog();
        }
        entity.setCode(dto.getCode());
        entity.setName(dto.getName());
        entity.setMaterialTypeCode(dto.getMaterialTypeCode());
        entity.setParentName(dto.getParentName());
        entity.setFullName(dto.getFullName());
        String parentCode = dto.getParentCode();

        if (StringUtils.isNotBlank(parentCode)) {
            MaterialCatalog parent = baseMapper.selectOne(Wrappers.<MaterialCatalog>query().eq("code", parentCode));
            /*if (parent == null) {
                synchronized (MaterialCatalogService.class) {
                    parent = baseMapper.selectOne(Wrappers.<MaterialCatalog>query().eq("code", parentCode));
                    CacheUtils.hashPut("data:base:materialCatalog:code", dto.getCode(), parent);
                }
            }*/

            entity.setParentId(parent == null ? "0" : parent.getId());
            entity.setParentIds(parent == null ? ",0," : parent.getParentIds() + parent.getId() + ",");
        }

        /**
         * 通过缓存获取 子级机构的数量，避免发送大量SQL
         */
        String parentId = StringUtils.defaultIfBlank(entity.getParentId(), "0");
        Integer childCount = (Integer) CacheUtils.get(materialCatalogChildSort, parentId);

        if (childCount == null) {
            synchronized (MaterialCatalogService.class) {
                childCount = this.count(Wrappers.<MaterialCatalog>query().eq("parent_id", parentId));
                childCount = childCount == null ? 0 : childCount;
            }
        }
        entity.setSort((childCount + 1L));
//        entity.setSorts(parent == null ? String.valueOf(entity.getSort()) : parent.getSorts() + "_" + entity.getSort());
        CacheUtils.put(materialCatalogChildSort, parentId, childCount + 10);

        BeanValidators.validateWithException(validator, entity);
        this.saveOrUpdate(entity);

        List<TreeNode> children = node.getChildren();
        if (!CollectionUtils.isEmpty(children)) {
            for (TreeNode child : children) {
                addCatalogByNode(child);
            }
        }
    }

    @Override
    public boolean removeCascadeBatchByIds(Collection<? extends Serializable> idList) {
        //是否有下属商品
        int count = this.count(Wrappers.<MaterialCatalog>query().in("parent_id", idList));
        if (count > 0) {
            throw new BusinessException("所选品类存在下级，不允许删除");
        }

        if (SpringUtils.getBean(MaterialService.class).count(Wrappers.<Material>query().in("catalog_id", idList)) > 0) {
            BusinessException.throwError("所选品类已有相关商品，不允许删除");
        }

        return super.removeCascadeBatchByIds(idList);
    }
}
